﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ExpressionEvaluator.Anonymous;

namespace ExpressionEvalTest
{
    [TestClass]
    public class AnonymousTypeBuilderTest
    {
        private const string genericArg1 = "Text";
        private const string genericArg2 = "Lenght";

        [TestMethod]
        public void Test_if_type_is_generics()
        {
            AnonymousArgumentProcessor arguments = new AnonymousArgumentProcessor(genericArg1, genericArg2);
            AnonymousTypeBuilder anon = new AnonymousTypeBuilder("<>f__AnonymousType0", genericArg1, genericArg2);
            Type t = anon.GenerateAnonymousType();
            var genericArguments = t.GetGenericArguments();
            Assert.AreEqual(arguments[0].GenericArgumentName, genericArguments[0].Name);
            Assert.AreEqual(arguments[1].GenericArgumentName, genericArguments[1].Name);
        }

        [TestMethod]
        public void Test_constructor_initialize_fields()
        {
            AnonymousArgumentProcessor arguments = new AnonymousArgumentProcessor(genericArg1, genericArg2);
            AnonymousTypeBuilder anon = new AnonymousTypeBuilder("<>f__AnonymousType0", genericArg1, genericArg2);
            Type gt = anon.GenerateAnonymousType();
            Type t = gt.MakeGenericType(typeof(string), typeof(int));
            object val = Activator.CreateInstance(t, "test", 5);
            Assert.AreEqual("test", t.GetField(arguments[0].GenericFieldName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(val));
            Assert.AreEqual(5, t.GetField(arguments[1].GenericFieldName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(val));
        }

        [TestMethod]
        public void Test_properties()
        {
            AnonymousArgumentProcessor arguments = new AnonymousArgumentProcessor(genericArg1, genericArg2);
            AnonymousTypeBuilder anon = new AnonymousTypeBuilder("<>f__AnonymousType0", genericArg1, genericArg2);
            Type gt = anon.GenerateAnonymousType();
            Type t = gt.MakeGenericType(typeof(string), typeof(int));
            object val = Activator.CreateInstance(t, "test", 5);
            Assert.AreEqual("test", t.GetProperty(arguments[0].Name).GetValue(val, null));
            Assert.AreEqual(5, t.GetProperty(arguments[1].Name).GetValue(val, null));
        }

        [TestMethod]
        public void Test_GetHashCode()
        {
            AnonymousArgumentProcessor arguments = new AnonymousArgumentProcessor(genericArg1, genericArg2);
            AnonymousTypeBuilder anon = new AnonymousTypeBuilder("<>f__AnonymousType0", genericArg1, genericArg2);
            Type gt = anon.GenerateAnonymousType();
            Type t = gt.MakeGenericType(typeof(string), typeof(int));
            var getHashCode = t.GetMethod("GetHashCode");
            object val = Activator.CreateInstance(t, "test", 5);
            object val2 = Activator.CreateInstance(t, "test", 6);
            object sameAsVal = Activator.CreateInstance(t, "test", 5);
            Assert.AreNotEqual(getHashCode.Invoke(val, null), getHashCode.Invoke(val2, null));
            Assert.AreEqual(getHashCode.Invoke(val, null), getHashCode.Invoke(sameAsVal, null));
        }

        [TestMethod]
        public void Test_Equals()
        {
            AnonymousArgumentProcessor arguments = new AnonymousArgumentProcessor(genericArg1, genericArg2);
            AnonymousTypeBuilder anon = new AnonymousTypeBuilder("<>f__AnonymousType0", genericArg1, genericArg2);
            Type gt = anon.GenerateAnonymousType();
            Type t = gt.MakeGenericType(typeof(string), typeof(int));
            var equals = t.GetMethod("Equals");
            object val = Activator.CreateInstance(t, "test", 5);
            object val2 = Activator.CreateInstance(t, "test", 6);
            object sameAsVal = Activator.CreateInstance(t, "test", 5);
            Assert.IsFalse((bool)equals.Invoke(val, new object[] { val2 }));
            Assert.IsTrue((bool)equals.Invoke(val, new object[] { sameAsVal }));
        }

        [TestMethod]
        public void Test_ToString()
        {
            AnonymousArgumentProcessor arguments = new AnonymousArgumentProcessor(genericArg1, genericArg2);
            AnonymousTypeBuilder anon = new AnonymousTypeBuilder("<>f__AnonymousType0", genericArg1, genericArg2);
            Type gt = anon.GenerateAnonymousType();
            Type t = gt.MakeGenericType(typeof(string), typeof(int));
            var toString = t.GetMethod("ToString", Type.EmptyTypes);
            object val = Activator.CreateInstance(t, "test", 5);
            object val2 = Activator.CreateInstance(t, "test", 6);
            object val3 = Activator.CreateInstance(t, "othertest", 5);
            Assert.AreEqual("{ Text = test, Lenght = 5 }", toString.Invoke(val, null));
            Assert.AreEqual("{ Text = test, Lenght = 6 }", toString.Invoke(val2, null));
            Assert.AreEqual("{ Text = othertest, Lenght = 5 }", toString.Invoke(val3, null));
        }
    }
}

